<HEAD>
  <SCRIPT SRC="../ganja.js"></SCRIPT>
</HEAD>
<BODY><SCRIPT>
// Create a Clifford Algebra with 2,0,1 metric.
Algebra(3,0,1,()=>{
  // First three Huzita-Hatori axioms, rotor and drawing helper.
  var Ori1 = (p1,p2)=>p1&p2&1e023,
      Ori2 = (p1,p2)=>(p1&p2)<<(p1+p2).Normalized,
      Ori3 = (l1,l2,flag)=>{ l1=l1.Normalized; l2=l2.Normalized; return flag==0?(l1+l2)&1e023:((l1+l2)<<(l1&1e023^l2))},
      rotor = (line,angle)=>Math.cos(angle/2) + Math.sin(angle/2)*line.Normalized,
      stroke = (O)=>[].concat.apply(O.concat([0x666666]),O.map(p=>p.map((e,i,a)=>[e,a[(i+1)%a.length]]))).concat(0xFFFFFF);
  
  // Quick and dirty origami fold function. Ignores adjacency/layering. (only performs splitting).
  // Takes a figure O, fold plane l, list of faces to fold f and angle a. Returns updated O. 
  var Ori = (O,l,f,a) => {
      var res=[]; O.forEach((s,si)=>{ 
        if (f.indexOf(si)==-1) return res.push(s);                              // no split needed
        var signs = s.map(p=>Math.sign(((p/p.e123)^l).e0123.toFixed(1)));       // side of ray points are on
        var t=[]; s.forEach((x,i)=>{                                            // loop over edges
            var j=(i+1)%s.length; t.push(x);                                    // insert vertex
            if (signs[i]&&signs[j]&&signs[i]!=signs[j]) t.push(x&s[j]^l)        // insert cut point
        });
        signs = t.map(p=>Math.sign(((p/p.e123)^l).e0123.toFixed(1)));             // recalc signs
        res.push(t.filter((p,i)=>signs[i]<=0).map(x=>rotor(l^1e1,a)>>>x).map(x=>x/x.e123),    // push turned face
                 t.filter((p,i)=>signs[i]>=0).map(x=>x/x.e123));                 // push split face
      })
      return res;
  }

  // Some helper points and our state.
  var point = (x,y)=>1e123+x*1e013-y*1e012;
  var s=2, p1=point(-s,s), p2=point(-s,-s), p3=point(s,-s), p4=point(s,s),
      p5=(p1+p3)/2, p6=(p4+p1)/2, p7=(p1+p2)/2, p8=(p1+p5)/2, p9=(p5+p6)/2, p10=(p5+p7)/2, p11=(p5+p8)/2,
      start=performance.now(), camera=0e0, step=0, cur, last, lastmove;
  
  document.body.appendChild(this.graph(()=>{
   // Animate camera, update step (time)
     var angle=Math.PI/4-Math.sin((performance.now()-start)/4000)/2;
     camera.set(Math.cos(angle)+Math.sin(angle)*1e13);
     step = (step + 1) % (13*100);
   // Figure out which move we are doing - if new move, set last origami to current one.
     var move = Math.floor((step/100)%13), timeMove = Math.min(1,0.01+((step/100)%13)-move);
     if (move != lastmove) { last=move?cur:[[p1,p2,p3,p4]]; lastmove=move; }
   // Calculate and render the current move based on the last finished origami and the current time.
     switch (move) {
       case  0: cur = Ori(last,Ori2(p1,p3),       [0],       timeMove*Math.PI); break; 
       case  1: cur = Ori(last,Ori2(p4,p3),       [0,1],     timeMove*Math.PI); break; 
       case  2: cur = Ori(last,Ori2(p2,p3),       [1,3],     timeMove*Math.PI); break;
       case  3: cur = Ori(last,Ori2(p5,p1),       [0,1,3,4], timeMove*Math.PI); break;
       case  4: cur = Ori(last,Ori2(p6,p5),       [7,8],     (1-Math.abs(timeMove*2-1))*Math.PI); break;
       case  5: cur = Ori(last,Ori2(p7,p5),       [5,6],     (1-Math.abs(timeMove*2-1))*Math.PI); break;
       case  6: cur = Ori(last,Ori3(p8&p10,p5&p8),[7,5],     timeMove*Math.PI); break;
       case  7: cur = Ori(last,Ori3(p9&p8,p8&p5), [13,11],   timeMove*Math.PI); break;
       case  8: cur = Ori(last,Ori2(p11,p1),      [17],      timeMove*Math.PI); break;
       case  9: cur = Ori(last,Ori1(p6,p7),       [17,18],   timeMove*Math.PI); break;
       case 10: cur = Ori(last,Ori1(p6,p7),       [4],      -timeMove*Math.PI); break;
     }
     return ["Folding Kabuto "+Math.min(11,(move+1))+"/11",0xFFFFFF].concat(stroke(cur+1e012+.5e013));
  },{animate:true,camera}));
});
</SCRIPT></BODY>